/*
 * @(#)SentinelFilePersistence.java 2021年3月10日
 *
 * Copyright (c) 2010 by rayootech.com. All rights reserved.
 */
package com.nuctech.config;

import java.io.File;
import java.io.IOException;
import java.util.List;

import org.springframework.core.env.Environment;

import com.alibaba.csp.sentinel.command.handler.ModifyParamFlowRulesCommandHandler;
import com.alibaba.csp.sentinel.datasource.Converter;
import com.alibaba.csp.sentinel.datasource.FileRefreshableDataSource;
import com.alibaba.csp.sentinel.datasource.FileWritableDataSource;
import com.alibaba.csp.sentinel.datasource.ReadableDataSource;
import com.alibaba.csp.sentinel.datasource.WritableDataSource;
import com.alibaba.csp.sentinel.init.InitFunc;
import com.alibaba.csp.sentinel.slots.block.authority.AuthorityRule;
import com.alibaba.csp.sentinel.slots.block.authority.AuthorityRuleManager;
import com.alibaba.csp.sentinel.slots.block.degrade.DegradeRule;
import com.alibaba.csp.sentinel.slots.block.degrade.DegradeRuleManager;
import com.alibaba.csp.sentinel.slots.block.flow.FlowRule;
import com.alibaba.csp.sentinel.slots.block.flow.FlowRuleManager;
import com.alibaba.csp.sentinel.slots.block.flow.param.ParamFlowRule;
import com.alibaba.csp.sentinel.slots.block.flow.param.ParamFlowRuleManager;
import com.alibaba.csp.sentinel.slots.system.SystemRule;
import com.alibaba.csp.sentinel.slots.system.SystemRuleManager;
import com.alibaba.csp.sentinel.transport.util.WritableDataSourceRegistry;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.TypeReference;
import com.nuctech.common.utils.SpringContextHolder;

/**
 * Sentinel规则持久化至本地文件
 * 
 * @author lilu
 * @date 2021年3月10日
 * @since 1.0.0
 */
public class SentinelFilePersistence implements InitFunc {

	@Override
	public void init() throws Exception {
		Environment environment = SpringContextHolder.getBean(Environment.class);
		
		String appcationName = environment.getProperty("spring.application.name");
		String userHome = environment.getProperty("user.home");
		
		String ruleDir = userHome + "/sentinel-rules/" + appcationName ;
		String flowRulePath = ruleDir + "/flow-rule.json";
		String degradeRulePath = ruleDir + "/degrade-rule.json";
		String systemRulePath = ruleDir + "/system-rule.json";
		String authorityRulePath = ruleDir + "/authority-rule.json";
		String paramFlowRulePath = ruleDir + "/param-flow-rule.json";

		this.mkdirIfNotExits(ruleDir);
		this.createFileIfNotExits(flowRulePath);
		this.createFileIfNotExits(degradeRulePath);
		this.createFileIfNotExits(systemRulePath);
		this.createFileIfNotExits(authorityRulePath);
		this.createFileIfNotExits(paramFlowRulePath);

		// 流控规则
		ReadableDataSource<String, List<FlowRule>> flowRuleRDS = new FileRefreshableDataSource<>(flowRulePath,
				flowRuleListParser);
		FlowRuleManager.register2Property(flowRuleRDS.getProperty());
		WritableDataSource<List<FlowRule>> flowRuleWDS = new FileWritableDataSource<>(flowRulePath, this::encodeJson);
		WritableDataSourceRegistry.registerFlowDataSource(flowRuleWDS);

		// 降级规则
		ReadableDataSource<String, List<DegradeRule>> degradeRuleRDS = new FileRefreshableDataSource<>(degradeRulePath,
				degradeRuleListParser);
		DegradeRuleManager.register2Property(degradeRuleRDS.getProperty());
		WritableDataSource<List<DegradeRule>> degradeRuleWDS = new FileWritableDataSource<>(degradeRulePath,
				this::encodeJson);
		WritableDataSourceRegistry.registerDegradeDataSource(degradeRuleWDS);

		// 系统规则
		ReadableDataSource<String, List<SystemRule>> systemRuleRDS = new FileRefreshableDataSource<>(systemRulePath,
				systemRuleListParser);
		SystemRuleManager.register2Property(systemRuleRDS.getProperty());
		WritableDataSource<List<SystemRule>> systemRuleWDS = new FileWritableDataSource<>(systemRulePath,
				this::encodeJson);
		WritableDataSourceRegistry.registerSystemDataSource(systemRuleWDS);

		// 授权规则
		ReadableDataSource<String, List<AuthorityRule>> authorityRuleRDS = new FileRefreshableDataSource<>(
				authorityRulePath, authorityRuleListParser);
		AuthorityRuleManager.register2Property(authorityRuleRDS.getProperty());
		WritableDataSource<List<AuthorityRule>> authorityRuleWDS = new FileWritableDataSource<>(authorityRulePath,
				this::encodeJson);
		WritableDataSourceRegistry.registerAuthorityDataSource(authorityRuleWDS);

		// 热点参数规则
		ReadableDataSource<String, List<ParamFlowRule>> paramFlowRuleRDS = new FileRefreshableDataSource<>(
				paramFlowRulePath, paramFlowRuleListParser);
		ParamFlowRuleManager.register2Property(paramFlowRuleRDS.getProperty());
		WritableDataSource<List<ParamFlowRule>> paramFlowRuleWDS = new FileWritableDataSource<>(paramFlowRulePath,
				this::encodeJson);
		ModifyParamFlowRulesCommandHandler.setWritableDataSource(paramFlowRuleWDS);
	}

	private Converter<String, List<FlowRule>>		flowRuleListParser		= source -> JSON.parseObject(source,
			new TypeReference<List<FlowRule>>() {
																					});
	private Converter<String, List<DegradeRule>>	degradeRuleListParser	= source -> JSON.parseObject(source,
			new TypeReference<List<DegradeRule>>() {
																					});
	private Converter<String, List<SystemRule>>		systemRuleListParser	= source -> JSON.parseObject(source,
			new TypeReference<List<SystemRule>>() {
																					});

	private Converter<String, List<AuthorityRule>>	authorityRuleListParser	= source -> JSON.parseObject(source,
			new TypeReference<List<AuthorityRule>>() {
																					});

	private Converter<String, List<ParamFlowRule>>	paramFlowRuleListParser	= source -> JSON.parseObject(source,
			new TypeReference<List<ParamFlowRule>>() {
																					});

	private void mkdirIfNotExits(String filePath) throws IOException {
		File file = new File(filePath);
		if (!file.exists()) {
			file.mkdirs();
		}
	}

	private void createFileIfNotExits(String filePath) throws IOException {
		File file = new File(filePath);
		if (!file.exists()) {
			file.createNewFile();
		}
	}

	private <T> String encodeJson(T t) {
		return JSON.toJSONString(t);
	}
}
